#include "blisp.h"

Type String_t; /* location of String type object */

/* 
 * Intern string objects and string wrapper objects.
 * If an incoming string equals an already-interned
 * string or wrapper, return this interned string
 * instead of constructing a new one..
 */

#define STR_TAB STR_intern_table
static String *STR_intern_table = NULL;

#define STR_OBJ_TAB STR_obj_intern_table
static Object *STR_obj_intern_table = NULL;

Object *To_lstr(Object *o)
{
	const char *s;
	size_t n;
	if (string_p(o)) return o;
	s = To_cstr(o);
	n = strlen(s);
	return OString_new_n(s, n);
}

String *String_new_n(const char *s, size_t n)
{
	String *ss = NULL;
	assert(strlen(s) == n);
	HASH_FIND(hh, STR_TAB, s, n, ss);
	if (ss) return ss; /* if already hashed, return interned value */
	ss = GC_NEW(String);
	ss->len = n;
	ss->val = GC_STRDUP(s);
	HASH_ADD_KEYPTR(hh, STR_TAB, s, n, ss);
	return ss;
}

Object *OString_new_n(const char *s, size_t n)
{
	Object *o;
	assert(strlen(s) == n);
	HASH_FIND(hh, STR_OBJ_TAB, s, n, o);
	if (o) return o; /* if already hashed, return interned value */
	o = GC_NEW(Object);
	settype(o, TSTRING);
	setstring(o, String_new_n(s, n));
	HASH_ADD_KEYPTR(hh, STR_OBJ_TAB, s, n, o);
	return o;
}

SELF_EVAL_FUNC(String)

const char *String_to_cstr(Object *o)
{
	int len = getstrlen(o);
	const char *s = getstrval(o);
	/* if not NUL-terminated, make a new buffer that is so */
	if (s[len] != '\0')
	{
		char *s2 = GC_MALLOC_ATOMIC(len + 1);
		strncpy(s2, s, len);
		s2[len + 1] = '\0';
		return (const char *) s2;
	}
	return s;
}

int string_equal(Object *o1, Object *o2)
{
	if (getstrlen(o1) != getstrlen(o2)) return CFALSE;
	return strncmp(getstrval(o1), getstrval(o2), getstrlen(o1)) == 0;
}

Object *String_equal(Object *o1, Object *o2)
{
	return cbool_to_lbool(string_equal(o1, o2));
}
